/*
 * Copyright (c) 2025, 大连理工大学 (Dalian University of Technology)
 *
 * Licensed under the Mulan PSL v2.
 * You can use this file according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of the License at
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. SEE THE MULAN PSL v2
 * FOR MORE DETAILS.
 */

#pragma once

#include <exception>
#include <iostream>
#include <map>
#include <vector>

#include "highfive/H5File.hpp"

#include "DBManager/Database/HDF5Database.h"
#include "DBManager/IRepository/Base/IResultRepository.h"
#include "DBManager/Repository/HDF5/Base/RepositoryBase.h"

template <>
inline HighFive::DataType HighFive::create_datatype<SG::DBManager::HDF5::ResultDomainIndex> ()
{
    HighFive::CompoundType compoundType = {
        { "DOMAIN_ID", HighFive::create_datatype<SG::DataStructure::Common::I64> () },
        { "POSITION", HighFive::create_datatype<SG::DataStructure::Common::I64> () },
        { "LENGTH", HighFive::create_datatype<SG::DataStructure::Common::I64> () },
    };
    return compoundType;
}

SG_DBMANAGER_HDF5_NAMESPACE_OPEN
/**
 * @brief 处理HDF5数据库的基础类，提供对数据表的操作接口。
 *
 * @tparam T 表单中存储的数据类型
 */
template <class T>
class DBManager_DECLSPEC ResultRepositoryBase : public RepositoryBase, public virtual IResultRepository<T>
{
  public:
    /**
     * @brief 枚举类型，用于标识不同的数据表。
     */
    enum DATASET : std::uint8_t
    {
        MainDataset,        ///< 节点数据所在表单
        IndexDataset,       ///< 节点所有ID表单
        DomainIndexDataset  ///< domain所在表单，记录每个工况位置,长度
    };

    /**
     * @brief 构造函数，初始化ResultRepositoryBase对象。
     *
     * @param database HDF5数据库的引用
     */
    explicit ResultRepositoryBase (HDF5Database& database) : RepositoryBase (database) {};

    virtual ~ResultRepositoryBase () = default;

    void InitDataset () override
    {
        RepositoryBase::InitDataset ();

        if (m_datasetInfo[DomainIndexDataset].IsCreate ())
        {
            // 缓存domain信息
            this->GetDataset (DomainIndexDataset).read (m_domainIndex);
        }
    }

    /**
     * @brief   : 写出索引数据
     * @param   : datas
     */
    void WriteIndexData (std::vector<T>& datas)
    {
        std::vector<SG::DataStructure::Common::Id> idList;
        for (auto&& data : datas)
        {
            // 如果有eId 就是用eId
            if constexpr (has_eId<T>::value)
            {
                idList.emplace_back (data.m_eId);
            }
            // 如果有id 就是用id
            else if constexpr (has_id<T>::value)
            {
                idList.emplace_back (data.m_id);
            }
        }
        this->WriteData (GetIndexDatasetInfo (), idList);
    }

    /**
     * @brief 使用向量添加数据，tip: 针对相同的 ID 只能添加一次。
     */
    SG::DataStructure::Common::Status Add (SG::DataStructure::Common::Id domainId, std::vector<T>& entities) override
    {
        // 如果还未创建表就去创建
        if (!m_datasetInfo[MainDataset].IsCreate ())
        {
            CreateDatasetInFile ();
        }

        // 1. 整理数据、主表数据排序
        for (auto&& entity : entities)
        {
            entity.m_domainId = domainId;
        }
        std::sort (entities.begin (), entities.end ());

        // 查看之前是否有该域
        bool isExist = false;
        for (auto&& index : m_domainIndex)
        {
            if (index.DOMAIN_ID == domainId)
            {
                isExist = true;
                std::vector<T> buffer;

                this->GetDataset (this->GetMainDatasetInfo ())
                    .select ({ static_cast<size_t> (index.POSITION) }, { static_cast<size_t> (index.LENGTH) })
                    .read (buffer);
                // 把数据放到一起
                buffer.insert (buffer.end (), entities.begin (), entities.end ());
                // 排序
                std::sort (buffer.begin (), buffer.end ());
                // 去重
                auto last = std::unique (buffer.begin (), buffer.end ());
                buffer.erase (last, buffer.end ());
                // 重新规划大小
                index.LENGTH = buffer.size ();
                // 插入数据
                insert (domainId, buffer);
                break;
            }
        }

        if (!isExist)
        {
            m_domainIndex.emplace_back (domainId,
                                        static_cast<SG::DataStructure::Common::I64> (GetMainDatasetInfo ().GetRowCount ()),
                                        static_cast<SG::DataStructure::Common::I64> (entities.size ()));
            std::sort (m_domainIndex.begin (), m_domainIndex.end ());

            // 2. 添加保存主表信息 只能顺序往后添加
            WriteData (GetMainDatasetInfo (), entities);

            // 3. 保存Index
            WriteIndexData (entities);
        }

        CoverData (this->GetDatasetInfo (DomainIndexDataset), m_domainIndex);

        return SG::DataStructure::Common::Status::STATUS_Success;
    }

    SG::DataStructure::Common::Status insert (SG::DataStructure::Common::Id domainId, std::vector<T>& entities)
    {
        bool              insertFlag = false;
        ResultDomainIndex nextIndex;
        std::vector<T>    buffer;
        for (size_t i = 0; i < m_domainIndex.size (); i++)
        {
            auto& index = m_domainIndex[i];
            if (index.DOMAIN_ID == domainId)
            {
                insertFlag = true;
                // 如果后面还有数据的话，先读出来
                if (i + 1 != m_domainIndex.size ())
                {
                    nextIndex = m_domainIndex[i + 1];
                    this->GetDataset (this->GetMainDatasetInfo ())
                        .select ({ static_cast<size_t> (nextIndex.POSITION) }, { static_cast<size_t> (nextIndex.LENGTH) })
                        .read (buffer);
                    // 更新后面的数据位置
                    nextIndex.POSITION   = index.POSITION + index.LENGTH;
                    m_domainIndex[i + 1] = nextIndex;
                }
                // 把要插入的数据覆盖进去
                CoverLocalData (this->GetDatasetInfo (MainDataset), index.POSITION, entities);
                // 重置索引表位置索引
                GetIndexDatasetInfo ().SetRowCount (index.POSITION);
                // 保存Index
                WriteIndexData (entities);
            }
            else if (insertFlag)
            {
                if (i + 1 != m_domainIndex.size ())
                {
                    nextIndex = m_domainIndex[i + 1];
                    this->GetDataset (this->GetMainDatasetInfo ())
                        .select ({ static_cast<size_t> (nextIndex.POSITION) }, { static_cast<size_t> (nextIndex.LENGTH) })
                        .read (buffer);
                    // 更新后面的数据位置
                    nextIndex.POSITION   = index.POSITION + index.LENGTH;
                    m_domainIndex[i + 1] = nextIndex;
                }
                // 把插入数据时，先读出的数据覆盖进去
                CoverLocalData (this->GetDatasetInfo (MainDataset), index.POSITION, buffer);
                // 保存Index
                WriteIndexData (buffer);
            }
        }

        return SG::DataStructure::Common::Status::STATUS_Success;
    }

    /**
     * @brief   : 获取 对应domain的数据
     * @param   : domainId
     * @param   : entities
     * @return  :
     */
    SG::DataStructure::Common::Status FindByDomainId (SG::DataStructure::Common::Id domainId, std::vector<T>& _OUT entities) override
    {
        for (auto&& index : m_domainIndex)
        {
            if (domainId == index.DOMAIN_ID)
            {
                // 从主表中读取该域的所有数据
                this->GetDataset (this->GetMainDatasetInfo ())
                    .select ({ static_cast<size_t> (index.POSITION) }, { static_cast<size_t> (index.LENGTH) })
                    .read (entities);
                return SG::DataStructure::Common::Status::STATUS_Success;
            }
        }

        return SG::DataStructure::Common::Status::STATUS_Failure;
    }

    /**
     * @brief   : 获取 对应domain的数据，可使用eIdSet进一步对子集的主键进行筛选
     * @param   : domainId
     * @param   : entities
     * @param   : eIdSet 根据此列表内的数据进行筛选
     * @return  :
     */
    SG::DataStructure::Common::Status FindByDomainId (SG::DataStructure::Common::Id           domainId,
                                                      std::set<SG::DataStructure::Common::Id> eIdSet,
                                                      std::vector<T>& _OUT                    entities) override
    {
        entities.clear ();
        // 遍历所有的索引
        for (auto&& index : m_domainIndex)
        {
            if (domainId == index.DOMAIN_ID)
            {
                for (auto&& entityId : eIdSet)
                {
                    // 查找该域的 ID 索引
                    std::vector<SG::DataStructure::Common::Id> idList;

                    this->GetDataset (IndexDataset)
                        .select ({ static_cast<size_t> (index.POSITION) }, { static_cast<size_t> (index.LENGTH) })
                        .read (idList);

                    // 二分查找特定的 entityId
                    auto iter = std::lower_bound (idList.begin (), idList.end (), entityId);

                    if (iter != idList.end () && *iter == entityId)
                    {
                        // 计算对应主表中的索引
                        size_t indexInPrimary = std::distance (idList.begin (), iter) + index.POSITION;

                        // 从主表中读取实体数据
                        T entity;
                        this->GetDataset (this->GetMainDatasetInfo ()).select ({ indexInPrimary }, { 1 }).read (entity);

                        entities.emplace_back (entity);
                    }
                }
                break;
            }
        }

        return entities.size () > 0 ? SG::DataStructure::Common::Status::STATUS_Success : SG::DataStructure::Common::Status::STATUS_Failure;
    }

    /**
     * @brief 获取特定domain的实体总数。
     *
     * 此函数根据提供的domain ID 返回与之关联的实体总数。
     *
     * @param domainId domain的 ID。
     * @return 返回实体的数量。
     */
    std::size_t Size (SG::DataStructure::Common::Id domainId) override
    {
        for (auto& i : m_domainIndex)
        {
            if (domainId == i.DOMAIN_ID)
            {
                return i.LENGTH;
            }
        }
        return 0;
    }

    std::vector<ResultDomainIndex>& GetResultDomainIndex ()
    {
        return m_domainIndex;
    }

  private:
    // 成员变量
    std::vector<ResultDomainIndex> m_domainIndex;  ///< 记录domainid的起始位置与长度 算是缓存
};
SG_DBMANAGER_HDF5_NAMESPACE_CLOSE
